// Package api
// @Title api[简述该包的主要作用]
// @Description
// @Author  cnHuaShao
// @since v0.0.1
// @Version v1.0.0
// @Date 2024/4/21 23:23
// @Update 2024/4/21 23:23
package api

import (
	"FinancialManagement/src/base/mess"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"net/http/httputil"
	"reflect"
	"regexp"
)

// 可忽略的参数，白名单
var skipParamsForSQLInjectMap = map[string]int{
	"file":               1, // 文件
	"color":              1, // 数据库颜色
	"dbColor":            1,
	"dbHost":             1, // 数据库访问地址
	"dbPassword":         1, // 数据库密码
	"otherParam":         1, // 树编号、模式域名，逗号拼接
	"tabName":            1, // 所属表名
	"tsql":               1, // 手动执行的SQL
	"describe":           1, // 描述内容
	"ids":                1, // 排序号，使用逗号拼接
	"where":              1,
	"dacType":            1,
	"fields":             1, // 创建或编辑数据表时字段JSON
	"fieldCorresponding": 1, // 创建或编辑数据表时字段JSON
	"param":              1, // 请求参数
	"oldTable":           1, // 表名-old
	"name":               1, // 表名-new
	"startTime":          1, // 开始时间
	"endTime":            1, // 结束时间
	"content":            1, //
	"salesDate":          1,
	"purchaseDeparture":  1,
	"itemDate":           1,
	"sdiPureProfit":      1,
	"sdiDate":            1, // 销售日期
	"cipher":             1,
}

// CheckXSSSQLInject
// @Description: SQL注入检测
// @param c
func CheckXSSSQLInject(c *gin.Context) {
	// 1、获取所有参数
	body, err := httputil.DumpRequest(c.Request, true)
	if err != nil {
		err = fmt.Errorf("parse request body failed, err: %s", err)
		c.JSON(http.StatusInternalServerError, mess.ResErrMess(70070075101, err.Error()))
		c.Abort()
		return
	}
	// 2、判断是否为JSON类型，如是JSON类型，则使用如下检测逻辑，否则执行其他检测逻辑
	if bytes.Index(body, []byte("application/json")) != -1 { // 检查json参数
		// 2-1过滤http头部,获取body
		body = body[bytes.Index(body, []byte("User-Agent")):]
		index := bytes.Index(body, []byte("{"))
		if index != -1 { // 2-2判读是否找到body
			body = body[index:]
		} else {
			body = nil
		}
		if len(body) != 0 { // 2-3检查request json参数
			fmt.Println(string(body))
			m := make(map[string]interface{})
			err = json.Unmarshal(body, &m) // 2-4将截取出来的JSON转为切片类型
			if err != nil {
				err = fmt.Errorf("parse request body failed, err: %s", err.Error())
				c.JSON(http.StatusInternalServerError, mess.ResErrMess(70070075102, err.Error()))
				c.Abort()
				return
			}
			// 2-5读取json中所有的键值对进行逐一检查
			for k, v := range m {
				if skipParamsForSQLInjectMap[k] == 1 {
					continue
				}
				if reflect.TypeOf(v).String() == "string" { // 判断v的反射类型，如是string则执行如下检测
					if filteredSQLInject(v.(string)) {
						err := fmt.Errorf("sql注入攻击 %s", v)
						c.JSON(http.StatusBadRequest, mess.ResErrMess(70070075103, err.Error()))
						c.Abort()
						return
					}
				}
			}
		}
	}
	if c.Request.Form == nil { // 检查get和post中的参数
		c.Request.ParseMultipartForm(32 << 20)
	}
	for k, arr := range c.Request.Form {
		// 检查请求方式，根据请求方式进行解析参数
		if c.Request.Method != http.MethodGet {
			fmt.Printf("%s=%v&", k, arr)
		}
		// 检查白名单
		if skipParamsForSQLInjectMap[k] == 1 {
			continue
		}
		for _, v := range arr {
			if filteredSQLInject(v) {
				err = fmt.Errorf("检测到XSS或SQL注入攻击，请确认其请求有效性")
				c.JSON(http.StatusBadRequest, mess.ResErrMess(70070075104, err.Error()))
				c.Abort()
				return
			}
		}
	}
	c.Next()
}

// 正则过滤sql注入的方法
// 参数 : 要匹配的语句
func filteredSQLInject(toMatchStr string) bool {
	// 过滤 ‘
	// ORACLE 注解 --  /**/
	// 关键字过滤 update ,delete
	// 正则的字符串, 不能用 " " 因为" "里面的内容会转义
	str := `(?:')|(?:--)|(/\\*(|[\\n\\r])*?\\*/)|(\b(select|update|and|or|delete|insert|trancate|char|chr|into|substr|ascii|declare|exec|count|master|into|drop|execute)\b)`
	re, err := regexp.Compile(str)
	if err != nil {
		panic(err.Error())
		return false
	}
	xss := "[%--`~!@#$^&*()=|{}':;',\\[\\]<>/?~！@#￥……&*（）——|{}【】‘；：”“'。，、？]"
	re, err = regexp.Compile(xss)
	if err != nil {
		panic(err.Error())
		return false
	}
	return re.MatchString(toMatchStr)
}
